package br.mil.eb.pqrmnt7.scc.model.controleabastecimento;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.inject.Inject;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import br.mil.eb.pqrmnt7.scc.entity.OrdemAbastecimento;
import br.mil.eb.pqrmnt7.scc.entity.QuotaCombustivel;
import br.mil.eb.pqrmnt7.scc.entity.TipoCombustivel;
import br.mil.eb.pqrmnt7.scc.entity.controleabastecimento.CalculoSaldoCombustivelException;
import br.mil.eb.pqrmnt7.scc.entity.controleabastecimento.LinhaControleSaldoCombustivel;
import br.mil.eb.pqrmnt7.scc.entity.controleabastecimento.LinhaControleSaldoCombustivelEntity;
import br.mil.eb.pqrmnt7.scc.persistence.jpa.ordemabastecimento.OrdemAbastecimentoDAO;
import br.mil.eb.pqrmnt7.scc.persistence.jpa.quotacombustivel.QuotaCombustivelDAO;

public class CalculaSaldoCombustivel {

	private static final Log logger = LogFactory.getLog(CalculaSaldoCombustivel.class);
	
	@Inject
	private OrdemAbastecimentoDAO oaDAO;
	
	@Inject
	private QuotaCombustivelDAO qcDAO;	

	public Long calculaSaldoCombustivel(TipoCombustivel tipoCombustivel, Date anoMesReferencia) throws CalculoSaldoCombustivelException {

		logger.debug("Chamado o calcula saldo da classe " + getClass().getCanonicalName());
		
		//TODO obter a lista de ordems emitidas no anoMesReferencia
		List<OrdemAbastecimento> ordensAbastecimentos = oaDAO.recuperaComMesInformado(anoMesReferencia);

		long volumeGasolina = 0;
		long volumeDiesel = 0;

		for (OrdemAbastecimento oa : ordensAbastecimentos) {
			
			if (oa.isAbastecimentoGasolina()) {
				volumeGasolina += oa.getVolumeAbastecido();
			} else if (oa.isAbastecimentoDiesel()) {
				volumeDiesel += oa.getVolumeAbastecido();
			} else {
				throw new RuntimeException("Não há regra para o tipo de combustível: " + 
											 oa.getViatura().getTipoCombustivel());
			}

		}
		
		if (tipoCombustivel.isGasolina()) {
			logger.debug("volumeGasolina: " + volumeGasolina);
			return volumeGasolina;
		} else {
			logger.debug("volumeDiesel: " + volumeDiesel);
			return volumeDiesel;
		}

	}

	public Long calculaSaldoCombustivel(TipoCombustivel tipoCombustivel, Date dataInicio, Date dataFim) {

		logger.debug("Chamado o calcula saldo da classe " + getClass().getCanonicalName());
		logger.debug("Data Inicio: " + dataInicio);
		logger.debug("Data Fim: " + dataFim);

		long quotaGasolina = 0;
		long quotaDiesel = 0;
		
		// obtém a lista de quotas emitidas no período
		List<QuotaCombustivel> quotas = qcDAO.recuperaPorPeriodo(dataInicio, dataFim);
		
		for (QuotaCombustivel qc : quotas) {
			if (qc.getTipoCombustivel().isGasolina()) {
				quotaGasolina += qc.getVolume();
			} else if (qc.getTipoCombustivel().isDiesel()) {
				quotaDiesel += qc.getVolume();
			} else {
				throw new RuntimeException("Não há regra para o tipo de combustível: " + 
												qc.getTipoCombustivel());
			}
		}

		long volumeGasolina = 0;
		long volumeDiesel = 0;
		
		// obtém a lista de ordems emitidas no anoMesReferencia
		List<OrdemAbastecimento> ordens = oaDAO.recuperaPorPeriodo(dataInicio, dataFim);

		for (OrdemAbastecimento oa : ordens) {
			
			if (oa.isAbastecimentoGasolina()) {
				volumeGasolina += oa.getVolumeAbastecido();
			} else if (oa.isAbastecimentoDiesel()) {
				volumeDiesel += oa.getVolumeAbastecido();
			} else {
				throw new RuntimeException("Não há regra para o tipo de combustível: " + 
											 oa.getViatura().getTipoCombustivel());
			}
		}
		
		
		long saldoGasolina = quotaGasolina - volumeGasolina;
		long saldoDiesel = quotaDiesel - volumeDiesel;
		
		if (tipoCombustivel.isGasolina()) {
			logger.debug("saldoGasolina: " + saldoGasolina);
			return saldoGasolina;
		} else if (tipoCombustivel.isDiesel()) {
			logger.debug("saldoDiesel: " + saldoDiesel);
			return saldoDiesel;
		} else {
			throw new RuntimeException("Não há regra para o tipo de combustível: " + tipoCombustivel);
		}

	}

	public List<LinhaControleSaldoCombustivel> recuperaRegistrosDeControle(	Date dataInicio, Date dataFim) {
		logger.debug("Chamado o recuperaRegistrosDeControle da classe " + getClass().getCanonicalName());
		logger.debug("Data Inicio: " + dataInicio);
		logger.debug("Data Fim: " + dataFim);
		
		List<LinhaControleSaldoCombustivel> linhas = new ArrayList<LinhaControleSaldoCombustivel>();

		//TODO obter a lista de quotas emitidas no período
		List<QuotaCombustivel> quotas = qcDAO.recuperaPorPeriodo(dataInicio, dataFim);
		
		for (QuotaCombustivel qc : quotas) {
			
			LinhaControleSaldoCombustivel linha = new LinhaControleSaldoCombustivelEntity(qc);
			linha.setFonte(qc.getFonteCombustivel().getNome());
			
//			if (qc.getTipoCombustivel().isGasolina()) {
//				quotaGasolina += qc.getVolume();
//				linha.setSaldo(quotaGasolina);
//			} else if (qc.getTipoCombustivel().isDiesel()) {
//				quotaDiesel += qc.getVolume();
//				linha.setSaldo(quotaDiesel);
//			} else {
//				throw new RuntimeException("Não há regra para o tipo de combustível: " + 
//												qc.getTipoCombustivel());
//			}
			
			linhas.add(linha);
			
		}

		//TODO obter a lista de ordens emitidas no período
		List<OrdemAbastecimento> ordens = oaDAO.recuperaPorPeriodo(dataInicio, dataFim);

		for (OrdemAbastecimento oa : ordens) {
			
			LinhaControleSaldoCombustivel linha = new LinhaControleSaldoCombustivelEntity(oa);
			linha.setMotorista(oa.getMotorista().toString());
			linha.setViatura(oa.getViatura().toString());
			linha.setNumeroVale(oa.getNumero().toString());
			
//			if (oa.isAbastecimentoGasolina()) {
//				volumeGasolina += oa.getVolumeAbastecido();
//				saldoGasolina -= oa.getVolumeAbastecido();
//				linha.setSaldo(saldoGasolina);
//			} else if (oa.isAbastecimentoDiesel()) {
//				volumeDiesel += oa.getVolumeAbastecido();
//				saldoDiesel -= oa.getVolumeAbastecido();
//				linha.setSaldo(saldoDiesel);
//			} else {
//				throw new RuntimeException("Não há regra para o tipo de combustível: " + 
//											 oa.getViatura().getTipoCombustivel());
//			}
			
			linhas.add(linha);
			
		}
		
		Comparator<? super LinhaControleSaldoCombustivel> c = new Comparator<LinhaControleSaldoCombustivel>() {
			@Override
			public int compare(LinhaControleSaldoCombustivel o1, LinhaControleSaldoCombustivel o2) {
				return o1.getData().compareTo(o2.getData());
			}
		};
		
		Collections.sort(linhas, c);
		
		long quotaGasolina = 0;
		long quotaDiesel = 0;
		
		long saldoGasolina = quotaGasolina;
		long saldoDiesel = quotaDiesel;
		
		long volumeGasolina = 0;
		long volumeDiesel = 0;
		
		for (LinhaControleSaldoCombustivel linha : linhas) {
			if (linha.getFluxoCombustivel().isFluxoDeEntrada()) {
				QuotaCombustivel qc = (QuotaCombustivel) linha.getFluxoCombustivel();
				if (qc.getTipoCombustivel().isGasolina()) {
					quotaGasolina += qc.getVolume();
					saldoGasolina += qc.getVolume();
					linha.setSaldo(saldoGasolina);
				} else if (qc.getTipoCombustivel().isDiesel()) {
					quotaDiesel += qc.getVolume();
					saldoDiesel += qc.getVolume();
					linha.setSaldo(saldoDiesel);
				} else {
					throw new RuntimeException("Não há regra para o tipo de combustível: "	+ qc.getTipoCombustivel());
				}
			} else if (linha.getFluxoCombustivel().isFluxoDeSaida()) {
				OrdemAbastecimento oa = (OrdemAbastecimento) linha.getFluxoCombustivel();
				if (oa.isAbastecimentoGasolina()) {
					volumeGasolina += oa.getVolumeAbastecido();
					saldoGasolina -= oa.getVolumeAbastecido();
					linha.setSaldo(saldoGasolina);
				} else if (oa.isAbastecimentoDiesel()) {
					volumeDiesel += oa.getVolumeAbastecido();
					saldoDiesel -= oa.getVolumeAbastecido();
					linha.setSaldo(saldoDiesel);
				} else {
					throw new RuntimeException("Não há regra para o tipo de combustível: " + oa.getViatura().getTipoCombustivel());
				}
			} else {
				throw new RuntimeException("Não há regra para o tipo de fluxo: " + linha.getFluxoCombustivel());
			}
		}

		logger.debug("quotaGasolina :" + quotaGasolina);
		logger.debug("quotaDiesel :" + quotaDiesel);
		logger.debug("volumeGasolina :" + volumeGasolina);
		logger.debug("volumeDiesel :" + volumeDiesel);
		logger.debug("saldoGasolina :" + saldoGasolina);
		logger.debug("saldoDiesel :" + saldoDiesel);
		logger.debug("linhas.size() :" + linhas.size());
		
		return linhas;
		
	}

	public List<LinhaControleSaldoCombustivel> recuperaRegistrosDeControle(
			TipoCombustivel tipoCombustivel, Date dataInicio, Date dataFim) {
		
			logger.debug("Chamado o recuperaRegistrosDeControle da classe " + getClass().getCanonicalName());
			logger.debug("Data Inicio: " + dataInicio);
			logger.debug("Data Fim: " + dataFim);
			logger.debug("TipoCombustivel: " + tipoCombustivel);			
		
			List<LinhaControleSaldoCombustivel> linhas = recuperaRegistrosDeControle(dataInicio, dataFim);
			
			logger.debug("Linhas antes: " + linhas.size());
			
			for (Iterator<LinhaControleSaldoCombustivel> iterator = linhas.iterator(); iterator.hasNext();) {
				LinhaControleSaldoCombustivel linha = iterator.next();
				if (!linha.getFluxoCombustivel().getTipoCombustivel().equals(tipoCombustivel)) {
					iterator.remove();
				}
			}
			
			logger.debug("Linhas depois: " + linhas.size());
		
		return linhas;

	}
	
	
}
